/*
 * Copyright (c) 2017-2019, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description:
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2019/11/04
 *
 * History:
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

#include "osal_log.h"
#include "osal_common.h"
#include "lindesplayer.h"
#include "disp_wrapper.h"
#include "vdec_wrapper.h"
#include "lindcomponent.h"

#define SCREEN_W    720
#define SCREEN_H    1280
static int g_screen_w          = SCREEN_W;
static int g_screen_h          = SCREEN_H;
static char * g_h264_file      = NULL;
static int g_codec             = AV_ENC_TYPE_H264;    // av_enc_type_t
static int g_rotate            = ROTATE_DEGREE_NONE;
static int64_t g_duration_us   = 16667;  // 60fps

#define MAX_PLAYER      4
static int g_running           = 0;
static pthread_t g_input_tid   = 0;
static int g_multi_player      = 1;
static _handle_t g_esplayer[MAX_PLAYER] = {0};

static int esplayer_on_input_key()
{
    signed char key_val;
    int ret = wait_input_char(&key_val, 500000);    // 500ms time out
    if (ret < 0) {
        return -1;
    } else if (ret == 0) {
        if (g_running == 0) {
            return -1;
        }
        return 0;
    }

    if (key_val > 0 && key_val)
    {
        if (key_val == '\n') {
            printf("\n");
        } else {
            printf("********* key_val: %c ********** \n", key_val);
        }

        if (key_val == 'q' || key_val == 'Q')
        {
            g_running = 0;
            return -1;
        }
        else if (key_val == 'r')
        {
            lindesplayer_config_t cfg = {0};
            int i = 0;
            for (i = 0; i < MAX_PLAYER; i++)
            {
                if (g_esplayer[i]) {
                    esplayer_get_parameters(g_esplayer[i], &cfg);
                    cfg.rotate++;
                    cfg.rotate = cfg.rotate % 4;
                    esplayer_set_parameters(g_esplayer[i], &cfg);
                }
            }
            return 0;
        }
        else if (key_val == 'p')
        {
            lindesplayer_config_t cfg = {0};
            int i = 0;
            static int count = 0;
            for (i = 0; i < MAX_PLAYER; i++)
            {
                if (g_esplayer[i]) {
                    esplayer_get_parameters(g_esplayer[i], &cfg);
                    if (count % 2 == 0) {
                        cfg.disp_pos.width -= 32;
                        cfg.disp_pos.height -= 32;
                    } else {
                        cfg.disp_pos.width += 32;
                        cfg.disp_pos.height += 32;
                    }
                    esplayer_set_parameters(g_esplayer[i], &cfg);
                }
            }
            count++;
            return 0;
        }
    }

    return 0;
}

static void * esplayer_input_key_thread(void *param)
{
    while(1)
    {
        if (esplayer_on_input_key())
            break;
    }

    logd("input_thread exit ");

    return 0;
}

static int find_start_code_next(char * buf, int size, char ** next)
{
    int i = 0;
    char * p_data = buf;
    for(i = 4; i < size - 4; i++)
    {
        if (p_data[i] == 0
            && p_data[i+1] == 0
            && p_data[i+2] == 1)
        {
            *next = buf + i;
            return 1;
        }

        if (p_data[i] == 0
            && p_data[i+1] == 0
            && p_data[i+2] == 0
            && p_data[i+3] == 1)
        {
            *next = buf + i;
            return 1;
        }
    }

    return 0;
}

int sample_es_player(char * h264_file, int index)
{
    int ret = 0;
    _handle_t esplayer = (_handle_t)NULL;
    FILE * pFile = NULL;
    int len = 0;
    int file_len = 0;
    char * buf = NULL;

    g_esplayer[index] = esplayer_create();
    esplayer = g_esplayer[index];
    if (esplayer == (_handle_t)NULL)
    {
        loge("esplayer_create failed");
        return -1;
    }


    lindesplayer_config_t config = {0};
    config.rotate = g_rotate;
    config.venc_type = g_codec;
    switch(index){
        case -1:
            config.disp_pos.top = 0;
            config.disp_pos.left = 0;
            config.disp_pos.width = 0;
            config.disp_pos.height = 0;
            break;
        case 0:
            config.disp_pos.top = 0;
            config.disp_pos.left = 0;
            config.disp_pos.width = g_screen_w / 2;
            config.disp_pos.height = g_screen_h / 2;
            break;
        case 1:
            config.disp_pos.top = g_screen_h / 2;
            config.disp_pos.left = 0;
            config.disp_pos.width = g_screen_w / 2;
            config.disp_pos.height = g_screen_h / 2;
            break;
        case 2:
            config.disp_pos.top = 0;
            config.disp_pos.left = g_screen_w / 2;
            config.disp_pos.width = g_screen_w / 2;
            config.disp_pos.height = g_screen_h / 2;
            break;
        case 3:
            config.disp_pos.top = g_screen_h / 2;
            config.disp_pos.left = g_screen_w / 2;
            config.disp_pos.width = g_screen_w / 2;
            config.disp_pos.height = g_screen_h / 2;
            break;
        default:
            loge("error index: %d", index);
            goto __exit__;
    };

    if (g_multi_player == 1) {
        // force full screen display
        config.disp_pos.width = 0;
        config.disp_pos.height = 0;
    }

    esplayer_set_parameters(esplayer, &config);
    ret = esplayer_prepare(esplayer);
    if (ret != 0)
    {
        loge("esplayer_prepare failed");
        goto __exit__;
    }

    // read h264 es to decoder
    pFile = fopen(h264_file, "rb");
	if (pFile == NULL)
	{
		loge("open %s failed\n", h264_file);
        goto __exit__;
    }

    fseek(pFile, 0L, SEEK_END);
    file_len = ftell(pFile);
	logd("file length: %d(Bytes)", file_len);
    fseek(pFile, 0L, SEEK_SET);

    buf = (char *)malloc(file_len);
    if (buf == NULL)
    {
        loge("oom");
        goto __exit__;
    }

    len = fread(buf, file_len, 1, pFile);
    if (len <= 0)
    {
        loge("fread failed");
        goto __exit__;
    }

    char * data = buf;
    int data_size = file_len;
    char * next_frame = NULL;
    int frame_size = 0;

    int64_t last_time_us = get_tick_us();
    int64_t sleep_us = 0;
    while(g_running && find_start_code_next(data, data_size, &next_frame))
    {
        frame_size = next_frame - data;
        ret = esplayer_decode_es(esplayer, data, frame_size);
        if(ret != 0)
        {
            logd("send_data_to_decoder failed , pls try again");
            //usleep(30000);
        }
        else
        {
            sleep_us = g_duration_us - (get_tick_us() - last_time_us);
            logv("sleep_us : %lld", sleep_us);
            if (sleep_us > 0) {
                usleep(sleep_us);
            }
            last_time_us = get_tick_us();
            logv("send len : %d", frame_size);
        }
        data = next_frame;
        data_size -= frame_size;
    }

    // last frame
    esplayer_decode_es(esplayer, data, frame_size);

#if 0
    // set a black frame
    logi("TEST: set a black frame");
    esplayer_set_black_frame(esplayer);
#endif

__exit__:

    esplayer_destroy(esplayer);
    g_esplayer[index] = (_handle_t)NULL;

    if (pFile != NULL)
    {
        fclose(pFile);
        pFile = NULL;
    }

    if (buf != NULL)
    {
        free(buf);
        buf = NULL;
    }
    return 0;
}

static void * multi_player_thread(void *param)
{
    int index = *(int *)param;
    free(param);

    logi(" ********* player[%d] running ", index);

    sample_es_player(g_h264_file, index);

    logi(" ********* player[%d] exit ", index);

    int i = 0;
    for (i = 0; i < MAX_PLAYER; i++)
    {
        if (g_esplayer[index]) {
            break;
        }
    }
    if (i == MAX_PLAYER) {
        logd("all players exit");
        g_running = 0;
    }

    return 0;
}

static void print_usage(char * this)
{
    printf("\033[32m");
    printf("Usage: \n");
    printf("    %s [es_file] <options>\n", this);
    printf("    other options parameters: \n");
    printf("        -h [help]           : print this usage \n");
    printf("        -s [screen_w, h]    : screen width & height, default '1280 720' \n");
    printf("        -m [multi_player]   : number of player instance, '1, 2, 3, 4', default: 1 \n");
    printf("        -r [rotate]         : video rotation degree, '0, 90, 180, 270', default 0 \n");
    printf("        -f [fps]            : video frame rate, default 60 \n");
    printf("        -c [codec]          : video codec type, 'h264' or 'h265', default 'h264' \n");
    printf("    such as: %s /mnt/extsd/1.h264 \n", this);
    printf("         or: %s /mnt/extsd/1.h264 -m 4 -r 90 -f 30 -s 480 854 \n", this);
    printf("\033[0m\n");
}

#ifdef MELIS_OS
static int main_sample_esplayer(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
{
	char h264_file[128] = "/sdcard/test.h264";
    int i = 0;
    pthread_t tid[4] = {0};
    int ret = 0;
    int rotate = 0;
    int fps = 0;
    int codec = AV_ENC_TYPE_H264;

    if (argc == 1 || !strcmp(argv[1], "-h"))
    {
        print_usage(argv[0]);
        return 0;
    }
    else
    {
        strcpy(h264_file, argv[1]);

        for (i = 2; i < argc; )
        {
            if (!strcmp(argv[i], "-m"))
            {
                g_multi_player = atoi(argv[i + 1]);
                i += 2;
                if (g_multi_player > MAX_PLAYER) {
                    logw("multi players should less than %d", MAX_PLAYER);
                    g_multi_player = MAX_PLAYER;
                }
            }
            else if (!strcmp(argv[i], "-r"))
            {
                rotate = atoi(argv[i + 1]);
                i += 2;
            }
            else if (!strcmp(argv[i], "-f"))
            {
                fps = atoi(argv[i + 1]);
                i += 2;
            }
            else if (!strcmp(argv[i], "-c"))
            {
                if (!strcmp(argv[i + 1], "h264")) {
                    codec = AV_ENC_TYPE_H264;
                } else if (!strcmp(argv[i + 1], "h265")) {
                    codec = AV_ENC_TYPE_H265;
                }
                i += 2;
            }
            else if (!strcmp(argv[i], "-s"))
            {
                g_screen_w = atoi(argv[i + 1]);
                g_screen_h = atoi(argv[i + 2]);
                i += 3;
            }
            else
            {
                i++;
            }
        }
    }

    g_codec = codec;
    g_h264_file = h264_file;
    g_running = 1;
    switch(rotate)
    {
        case 0:
            g_rotate = ROTATE_DEGREE_NONE;
            break;
        case 90:
            g_rotate = ROTATE_DEGREE_90;
            break;
        case 180:
            g_rotate = ROTATE_DEGREE_180;
            break;
        case 270:
            g_rotate = ROTATE_DEGREE_270;
            break;
        default:
            g_rotate = ROTATE_DEGREE_NONE;
            logw("invalid rotate(%d), force to 0", rotate);
            break;
    }
    if (fps > 0) {
        g_duration_us = 1000000/fps;
    }
    logi("play file: %s, multi player: %d, rotate: %d", h264_file, g_multi_player, rotate);

    for (i = 0; i < g_multi_player; i++)
    {
        int * index = malloc(sizeof(int));
        *index = i;
        ret = pthread_create(&tid[i], NULL, multi_player_thread, (void *)index);
        if (ret) {
            loge("multi_player_thread fail! ret: %d ", ret);
            return -1;
        }
    }

    ret = pthread_create(&g_input_tid, NULL, esplayer_input_key_thread, NULL);
    if (ret) {
        loge("pthread_create msg thread fail! ret: %d ", ret);
        exit(-1);
    }
    pthread_join(g_input_tid, 0);

    for (i = 0; i < g_multi_player; i++)
    {
        if (tid[i]) {
            pthread_join(tid[i], 0);
        }
    }

    logd("main exit ok");
	return 0;
}

#ifdef MELIS_OS
FINSH_FUNCTION_EXPORT_ALIAS(main_sample_esplayer, __cmd_sample_esplayer, H264 es player);
#endif
